package cn.rdtimes.wolfdsp.core.service;

import cn.rdtimes.wolfdsp.core.conf.Configuration;
import cn.rdtimes.wolfdsp.core.data.DataService;
import cn.rdtimes.wolfdsp.core.ha.*;
import cn.rdtimes.wolfdsp.core.ids.DefaultIdsGenerator;
import cn.rdtimes.wolfdsp.core.ids.IdsGenerator;
import cn.rdtimes.wolfdsp.core.invoker.TaskInvokeHandler;
import cn.rdtimes.wolfdsp.core.invoker.TaskInvokeHandlerFactory;
import cn.rdtimes.wolfdsp.core.job.JobGraphService;
import cn.rdtimes.wolfdsp.core.job.JobGraphServiceImpl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * 调度管理者,是调度的主入口,可获取调度相关的所有接口
 * <p>
 * 需要完成以下接口的实现,有的是必须实现,有的是可选实现(使用内部缺省实现)
 * 1. Configuration 参数配置,可自行定义参数项,无需实现
 * 2. DataService 数据读取和存储服务接口,必须实现
 * 3. PlatformHAStrategy 平台高可用服务接口,必须实现
 * 4. IdsGenerator 各种乐行实例id 的生成服务接口,可选默认实现
 * 5. NameService 微服务名称获取所有服务器服务接口,必须实现
 * 6. JobGraphService 流程调度服务接口,可选默认实现
 * 7. StateSyncService 状态同步接口，可选默认实现
 *
 * @author BZ
 */
public final class ScheduleManager {
    private static final ScheduleManager instance = new ScheduleManager();

    private Configuration configuration;
    private DataService dataService;
    private PlatformHAStrategy haStrategy;
    private IdsGenerator idsGenerator;
    private NameService nameService;
    private JobGraphService jobGraphService;
    private StateSyncService stateSyncService;

    private volatile boolean isStarted;
    private List<Service> serviceSet;

    private ScheduleManager() {
        TaskInvokeHandlerFactory.createDefaultTaskInvokeHandler();
    }

    public static ScheduleManager getInstance() {
        return instance;
    }

    public PlatformHAStrategy getPlatformHAStrategy() {
        return haStrategy;
    }

    public void setPlatformHAStrategy(PlatformHAStrategy ha) {
        if (this.haStrategy != null) {
            throw new IllegalArgumentException("haStrategy is not null");
        }
        if (ha == null) {
            throw new NullPointerException("ha is null");
        }
        this.haStrategy = ha;
    }

    public void setStateSyncService(StateSyncService stateSyncService) {
        if (this.stateSyncService != null) {
            throw new IllegalStateException("stateSyncService is not null");
        }
        if (stateSyncService == null) {
            throw new NullPointerException("stateSyncService is null");
        }
        this.stateSyncService = stateSyncService;
    }

    public StateSyncService getStateSyncService() {
        return stateSyncService;
    }

    /**
     * 启动所有服务，必须先调用他，才能正常使用其他方法
     *
     * @throws Exception 失败抛出异常
     */
    public void start() throws Exception {
        if (isStarted) return;

        validate();
        Collections.sort(serviceSet);
        for (Service service : serviceSet) {
            service.start();
        }
        isStarted = true;

        registryShutdownHook();
    }

    private void validate() {
        if (getConfiguration() == null) {
            configuration = new Configuration();
        }
        if (getIdsGenerator() == null) {
            idsGenerator = new DefaultIdsGenerator();
        }
        if (getNameService() == null) {
            throw new IllegalStateException("nameService is null");
        }
        if (TaskInvokeHandlerFactory.entrySet().size() != 3) {
            throw new IllegalStateException("taskInvokeHandler need to hava 3 kinds");
        }
        if (getDataService() == null) {
            throw new IllegalStateException("dataService is null");
        }
        if (getPlatformHAStrategy() == null) {
            haStrategy = new DefaultPlatformHAStrategy(configuration.getHAStrategyState());
        }
        if (getJobGraphService() == null) {
            jobGraphService = new JobGraphServiceImpl();
        }
        if (getStateSyncService() == null) {
            stateSyncService = new DefaultStateSyncService();
        }

        if (serviceSet == null) serviceSet = new ArrayList<>(16);
        serviceSet.add(getIdsGenerator());
        serviceSet.add(getNameService());
        serviceSet.add(getDataService());
        serviceSet.add(getPlatformHAStrategy());
        serviceSet.add(getStateSyncService());
        serviceSet.add(getJobGraphService());
    }

    public void shutdown() {
        if (!isStarted) return;
        isStarted = false;

        Collections.sort(serviceSet);
        //按照相反的顺序停止服务对象，由高到低停止服务
        int count = serviceSet.size() - 1;
        for (int i = count; i >= 0; i--) {
            Service serverService = serviceSet.get(i);
            try {
                serverService.shutdown();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private void registryShutdownHook() {
        Runtime.getRuntime().addShutdownHook(new Thread() {
            public void run() {
                shutdown();
            }
        });
    }

    public void setConfiguration(Configuration conf) {
        if (this.configuration != null) {
            throw new IllegalStateException("configuration is not null");
        }
        if (conf == null) {
            throw new NullPointerException("configuration is null");
        }
        this.configuration = conf;
    }

    public Configuration getConfiguration() {
        return configuration;
    }

    public void setDataService(DataService ds) {
        if (this.dataService != null) {
            throw new IllegalStateException("dataService is not null");
        }
        if (ds == null) {
            throw new NullPointerException("dataService is null");
        }
        this.dataService = ds;
    }

    public DataService getDataService() {
        return dataService;
    }

    public void setJobGraphService(JobGraphService jobGraphService) {
        if (this.jobGraphService != null) {
            throw new IllegalStateException("jobGraphService is not null");
        }
        if (jobGraphService == null) {
            throw new NullPointerException("jobGraphService is null");
        }
        this.jobGraphService = jobGraphService;
    }

    public void setTaskInvokeHandler(Class<? extends TaskInvokeHandler<?, ?>> clazz) throws Exception {
        if (clazz == null) {
            throw new NullPointerException("clazz is null");
        }

        TaskInvokeHandler<?, ?> handler = clazz.newInstance();
        if (handler.getKind() == TaskInvokeHandler.InvokeKind.HTTP) {
            configuration.setHttpTaskInvokeHandlerId(handler.getName());
        } else if (handler.getKind() == TaskInvokeHandler.InvokeKind.JAR) {
            configuration.setJarTaskInvokeHandlerId(handler.getName());
        } else if (handler.getKind() == TaskInvokeHandler.InvokeKind.SHELL) {
            configuration.setShellTaskInvokeHandlerId(handler.getName());
        }
    }

    public void setIdsGenerator(IdsGenerator idsGenerator) {
        if (this.idsGenerator != null) {
            throw new IllegalStateException("idsGenerator is not null");
        }
        if (idsGenerator == null) {
            throw new NullPointerException("idsGenerator is null");
        }
        this.idsGenerator = idsGenerator;
    }

    public IdsGenerator getIdsGenerator() {
        return idsGenerator;
    }

    public void setNameService(NameService nameService) {
        if (this.nameService != null) {
            throw new IllegalStateException("nameService is not null");
        }
        if (nameService == null) {
            throw new NullPointerException("nameService is null");
        }
        this.nameService = nameService;
    }

    public NameService getNameService() {
        return nameService;
    }

    public JobGraphService getJobGraphService() {
        return jobGraphService == null ? new JobGraphServiceImpl() : jobGraphService;
    }

    public void setCompletedListener(JobGraphService.CompletedListener listener) {
        getJobGraphService().setCompletedListener(listener);
    }

}
